/**
 * 记录各企业微信的状态，处理状态的自行转变（不负责“发现企业微信”等行为）
 */
import { MemSubject } from '@gnlab/utils/rx'
import StorageManager from '@/lib/storage'
import type { TimingStatus, NoTimingStatus } from './types'
import { Status, timingStatus, PREPARING } from './types'

/**
 * 通过 storage 持久化记录企业微信状态
 * 按目前的设计，只有有时效的状态会被记录（TimingStatus）
 */
type StorageRecord = {
  status: TimingStatus
  until: number // 截止时间
  hostname: string // 进行记录时企业微信对应的 hostname。若后续发现企业微信的 hostname 变了，说明是重新登录过了，原记录失效。
}
type StorageRecords = { [wxid: string]: StorageRecord }

class StatusStorage extends StorageManager<StorageRecords> {
  constructor() {
    super({
      topic: 'service-qywxs-status',
      defaults: {},
      session: true,
    })

    this.initAutoClear()
  }

  /**
   * 判断记录是否过期
   */
  isOutdated(record: StorageRecord) {
    return 'until' in record && record.until <= Date.now()
  }

  /**
   * 返回指定企业微信的状态记录，若没有或已过期，返回 null
   */
  get(wxid: string, hostname: string) {
    const record = this.data[wxid]
    if (record) {
      if (record.hostname === hostname && !this.isOutdated(record)) return record
      this.remove(wxid) // 移除无效记录
    }
    return null
  }

  /**
   * 记录状态
   */
  mark(wxid: string, record: StorageRecord) {
    this.data[wxid] = record
    this.save(this.data)
  }

  /**
   * 移除状态记录
   */
  remove(wxid: string) {
    delete this.data[wxid]
    this.save(this.data)
  }

  /**
   * 清理过期数据
   */
  protected initAutoClear() {
    setInterval(() => this.clearRecords(), 1000 * 10)
  }
  protected clearRecords() {
    for (const [wxid, record] of Object.entries(this.data)) {
      if (this.isOutdated(record)) delete this.data[wxid]
    }
    this.save(this.data)
  }
}

const storage = new StatusStorage()

/**
 * 处理单个企业微信的状态变化
 *
 * - 检查 wxid 在 storage 里是否有记录（也就是刷新页面前是否记录过）
 * - 若记录为 preparing，则保持此状态
 * - 若记录为其他特殊状态，则保持此状态
 * - 如果 storage 里没有记录，则根据 preparing 参数决定进入 preparing 还是 confirming
 */
export class QywxStatus {
  // 当前状态
  current = new MemSubject<Status>(Status.Normal)

  // 状态截止时间到了时，收尾的回调的 id
  protected timeoutId: NodeJS.Timeout | null = null

  constructor(
    public wxid: string,
    public hostname: string,
    preparing = false,
  ) {
    // 确定企业微信初始状态
    const record = storage.get(this.wxid, this.hostname)
    if (preparing && record?.status !== Status.Preparing) {
      this.goStatus(Status.Preparing, Date.now() + PREPARING)
    } else if (record) {
      this.goStatus(record.status, record.until)
    } else {
      this.goStatus(Status.Normal)
    }
  }

  /**
   * 处理企业微信状态变化
   */
  goStatus(status: NoTimingStatus): void
  goStatus(status: TimingStatus, until: number): void
  goStatus(status: Status, until?: number) {
    // 移除之前状态的收尾回调
    if (this.timeoutId) {
      clearTimeout(this.timeoutId)
      this.timeoutId = null
    }

    // 更新状态记录
    if (timingStatus.has(status)) {
      storage.mark(this.wxid, {
        hostname: this.hostname,
        status: status as TimingStatus,
        until: until!,
      })
    } else {
      storage.remove(this.wxid)
    }

    // 更新状态
    this.current.next(status)

    // 注册截止时间回调
    if (timingStatus.has(status)) {
      this.timeoutId = setTimeout(() => {
        if (status === Status.Preparing) this.goStatus(Status.Normal)
        else this.goStatus(Status.Removed)
      }, until! - Date.now())
    }
  }
}
